#include "swegl/common.h"
#include "swegl/math.h"
#include "swegl/texture.h"
#include <memory.h>
#include <stdio.h>

namespace swegl
{

static void memset128bis(void *ptr, uint32 value, size_t size)
{
	__m128i input = _mm_set_epi32(value, value, value, value);
	__m128i *ptr128 = static_cast<__m128i *>(ptr);
	for (size_t i = 0, n = size / 16; i < n; i++)
	{
		_mm_store_si128(&ptr128[i], input);
	}
	/*
	__m128i *endptr128 = static_cast<__m128i *>(ptr) + size / 16;
	while (ptr128 != endptr128)
	{
		_mm_store_si128(ptr128++, input);
	}*/
}

static void memset128bis(void *ptr, float value, size_t size)
{
	memset128bis(ptr, *reinterpret_cast<uint32 *>(&value), size);
}

#  define SSE_MMREG_SIZE 16
static void memset128(void *ptr, float value, size_t size)
{
	// http://www.koders.com/cpp/fidDF243F74F3B3BF9949C5482B6EA27F714DB3C603.aspx
	__m128 input0 = _mm_set_ps1(value);
	__m128 input1 = _mm_set_ps1(value);
	__m128 input2 = _mm_set_ps1(value);
	__m128 input3 = _mm_set_ps1(value);

	char *p_dst = (char*)ptr;
	size_t delta;
	if( ( delta = ((size_t)p_dst) & ( SSE_MMREG_SIZE - 1 ) ) != 0 ) /* Align destinition */
	{
		delta = SSE_MMREG_SIZE - delta;
		size -= delta;
		memset( p_dst, 0, delta );
		p_dst += delta;
	}
	size_t i = size >> 6;
	size &= 63;
	for ( ; i; --i, p_dst += 64)
	{
		_mm_stream_ps((float *)p_dst +  0, input0);
		_mm_stream_ps((float *)p_dst +  4, input1);
		_mm_stream_ps((float *)p_dst +  8, input2);
		_mm_stream_ps((float *)p_dst + 12, input3);
	}
}

static void memset128(void *ptr, uint32 value, size_t size)
{
	memset128(ptr, *reinterpret_cast<float *>(&value), size);
}

Surface::Surface(uint width, uint height, uint32 *buffer) : m_width(width), m_height(height)
{
	if (buffer)
		m_data = buffer;
	else
		m_data = new uint32[width * height];
}

Surface::~Surface()
{
	delete [] m_data;
}

void Surface::clear(uint32 value)
{
	uint32 *b = m_data;
	for (size_t i = 0, n = m_width * m_height; i < n; i++)
		b[i] = value;
}

void Surface::clear(float value)
{
	float *b = reinterpret_cast<float *>(m_data);
	for (size_t i = 0, n = m_width * m_height; i < n; i++)
		b[i] = value;
}

Texture::Texture(uint width, uint height, uint numLevel)
{
	if (numLevel)
	{
		do
		{
			m_surfaces.push_back(new Surface(width, height));
			width = Max<uint>(width / 2, 1);
			height = Max<uint>(height / 2, 1);
		} while (--numLevel > 0 && width != 1 && height != 1);
	}
	else
	{
		do
		{
			m_surfaces.push_back(new Surface(width, height));
			width = Max<uint>(width / 2, 1);
			height = Max<uint>(height / 2, 1);
		} while (width != 1 && height != 1);
	}
}

Texture::~Texture()
{
	for (uint i = 0; i < m_surfaces.size(); i++)
	{
		delete m_surfaces[i];
	}
}

}
